#include <vector_types.h>

#include <algorithm/analytical_model/algorithm/cuda/ConvolutionMethod.cuh>
#include <algorithm/analytical_model/algorithm/cuda/inter_op/ConvolutionMethod.h>
#include <algorithm/analytical_model/algorithm/dnn/params/UNIZAR2.h>
#include <utils/Utils.h>

#include <bit>
#include <memory>

using namespace solar::cuda::inter_op;

ConvolutionMethod::ConvolutionMethod(Float3* helio_verts, Float3 helio_normal, Float3 helio_pos,
                                     Float3 recv_pos, Float3 helio_size, Float3 recv_size,
                                     Float3 focus_center, Int2 helio_row_col, Int2 recv_row_col,
                                     bool* d_micro_sb, ConvolutionModelType mode, float* d_array,
                                     cudaStream_t stream)
{
    auto* helio_verts_ptr = std::bit_cast<float3*>(helio_verts);
    this->delegate_ptr_ = std::make_shared<::solar::cuda::ConvolutionMethod>(
        helio_verts_ptr, structCopy<float3>(helio_normal), structCopy<float3>(helio_pos),
        structCopy<float3>(recv_pos), structCopy<float3>(helio_size), structCopy<float3>(recv_size),
        structCopy<float3>(focus_center), structCopy<int2>(helio_row_col),
        structCopy<int2>(recv_row_col), d_micro_sb, mode, d_array, stream);
}

ConvolutionMethod::ConvolutionMethod(Float3* helio_verts, Float3 helio_normal,
                                     Float3 helio_position, Float3 focus_center, Int2 helio_row_col,
                                     Int2 recv_row_col, Float3* recv_verts, Float3 recv_normal,
                                     bool* d_micro_sb, float* d_array, cudaStream_t stream)
{
    auto* helio_verts_ptr = std::bit_cast<float3*>(helio_verts);
    auto* recv_verts_ptr = std::bit_cast<float3*>(recv_verts);
    this->delegate_ptr_ = std::make_shared<::solar::cuda::ConvolutionMethod>(
        helio_verts_ptr, structCopy<float3>(helio_normal), structCopy<float3>(helio_position),
        structCopy<float3>(focus_center), structCopy<int2>(helio_row_col),
        structCopy<int2>(recv_row_col), recv_verts_ptr, structCopy<float3>(recv_normal), d_micro_sb,
        d_array, stream);
}

ConvolutionMethod::ConvolutionMethod(Float3* d_image_plane_xyz_axes, Float3* d_helio_verts,
                                     bool* d_micro_sb, bool* d_micro_sb_per_row,
                                     Float3* d_flux_params, Float3* d_focus_centers,
                                     std::size_t num_helios, Float3 recv_size,
                                     Float3 recv_pos_center, Int2 helio_row_col, Int2 recv_row_col,
                                     ConvolutionModelType mode, float* d_array, cudaStream_t stream)
{
    auto* d_image_plane_xyz_axes_ptr = std::bit_cast<float3*>(d_image_plane_xyz_axes);
    auto* d_helio_verts_ptr = std::bit_cast<float3*>(d_helio_verts);
    auto* d_flux_params_ptr = std::bit_cast<float3*>(d_flux_params);
    auto* d_focus_centers_ptr = std::bit_cast<float3*>(d_focus_centers);
    this->delegate_ptr_ = std::make_shared<::solar::cuda::ConvolutionMethod>(
        d_image_plane_xyz_axes_ptr, d_helio_verts_ptr, d_micro_sb, d_micro_sb_per_row,
        d_flux_params_ptr, d_focus_centers_ptr, num_helios, structCopy<float3>(recv_size),
        structCopy<float3>(recv_pos_center), structCopy<int2>(helio_row_col),
        structCopy<int2>(recv_row_col), mode, d_array, stream);
}

ConvolutionMethod::ConvolutionMethod(Float3* d_image_plane_xyz_axes, Float3* d_helio_verts,
                                     bool* d_micro_sb, bool* d_micro_sb_per_row,
                                     ::solar::dnn::UNIZAR2Output* d_model_outputs,
                                     Float3* d_focus_centers, std::size_t num_helios,
                                     Float3 recv_size, Float3 recv_pos_center, Int2 helio_row_col,
                                     Int2 recv_row_col, ConvolutionModelType mode, float* d_array,
                                     cudaStream_t stream)
{
    auto* d_image_plane_xyz_axes_ptr = std::bit_cast<float3*>(d_image_plane_xyz_axes);
    auto* d_helio_verts_ptr = std::bit_cast<float3*>(d_helio_verts);
    auto* d_focus_centers_ptr = std::bit_cast<float3*>(d_focus_centers);
    this->delegate_ptr_ = std::make_shared<::solar::cuda::ConvolutionMethod>(
        d_image_plane_xyz_axes_ptr, d_helio_verts_ptr, d_micro_sb, d_micro_sb_per_row,
        d_model_outputs, d_focus_centers_ptr, num_helios, structCopy<float3>(recv_size),
        structCopy<float3>(recv_pos_center), structCopy<int2>(helio_row_col),
        structCopy<int2>(recv_row_col), mode, d_array, stream);
}

void ConvolutionMethod::drawFluxMapCylinder() const { this->delegate_ptr_->drawFluxMapCylinder(); }

void ConvolutionMethod::drawFluxMapCylinderFast() const
{
    this->delegate_ptr_->drawFluxMapCylinderFast();
}

void ConvolutionMethod::drawFluxMap() const { this->delegate_ptr_->drawFluxMap(); }

void ConvolutionMethod::drawFluxMapFast() const { this->delegate_ptr_->drawFluxMapFast(); }

auto ConvolutionMethod::getFluxMap() const -> float* { return this->delegate_ptr_->d_array_; }

auto ConvolutionMethod::loadGlParamImpl(int n) const -> bool
{
    return this->delegate_ptr_->loadGlParam(n);
}

void ConvolutionMethod::setCudaStream(const cudaStream_t& stream) const
{
    this->delegate_ptr_->setCudaStream(stream);
}

auto ConvolutionMethod::getCudaStream() const -> cudaStream_t
{
    return this->delegate_ptr_->stream_;
}

void ConvolutionMethod::setModelParams(float sigma1, float sigma2, float peak) const
{
    this->delegate_ptr_->sigma1_ = sigma1;
    this->delegate_ptr_->sigma2_ = sigma2;
    this->delegate_ptr_->flux_param_ = peak;
}

void ConvolutionMethod::setModelParams(const dnn::UNIZAR2Output& params) const
{
    this->delegate_ptr_->sigma1_ = params.sigma_x_;
    this->delegate_ptr_->sigma2_ = params.sigma_y_;
    this->delegate_ptr_->flux_param_ = params.peak_flux_param_;
    this->delegate_ptr_->x3_ = params.x3_;
    this->delegate_ptr_->x0_ = params.x0_;
    this->delegate_ptr_->y1_ = params.y1_;
    this->delegate_ptr_->y0_ = params.y0_;
    this->delegate_ptr_->external_heliostat_projection_pos_ = true;
}

void ConvolutionMethod::setHeliostatID(std::uint64_t helio_id) const
{
    this->delegate_ptr_->helio_id_ = helio_id;
}
